package com.testing.sunnycommon.controller;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.testing.sunnycommon.model.Result;
import com.testing.sunnycommon.model.SunnyUser;
import com.testing.sunnycommon.security.jwt.JwtUtils;
import com.testing.sunnycommon.service.SunnyUserService;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;

/**
 * @Classname SunnyUserController
 * @Description 类型说明
 * @Date 2023/6/30 21:41
 * @Created by 特斯汀Roy
 */
@RestController
@RequestMapping("/sunnyUser")
@Slf4j
public class SunnyUserController {

    @Autowired
    SunnyUserService sunnyUserService;

    @Autowired
    AuthenticationManager authenticationManager;

    @Autowired
    JwtUtils jwtUtils;

    @GetMapping
    public Result getSunnyUser(@RequestHeader(JwtUtils.HEADER_STRING) String token) {
        String usernameFromToken = jwtUtils.getUsernameFromToken(token.substring(JwtUtils.TOKEN_PREFIX.length()));
        SunnyUser sunnyUser = sunnyUserService.loadUserByUsername(usernameFromToken);
        sunnyUser.setPassword("*************");
        return Result.success(sunnyUser);
    }

    @PostMapping("/login")
    public Result login(@RequestBody SunnyUser user, HttpServletResponse response) {
        SunnyUser existUser = sunnyUserService.loadUserByUsername(user.getUsername());
        if (existUser == null) {
            return Result.fail(1011, "登录失败，用户不存在", user.getUsername());
        }
        //在完成登录之前获取一下securityContext中存储的鉴权信息。
        System.out.println("securityContext中存储的鉴权信息" + SecurityContextHolder.getContext());
        if (SecurityContextHolder.getContext().getAuthentication() != null && SecurityContextHolder.getContext().getAuthentication().getPrincipal().getClass().equals(SunnyUser.class)) {
            return Result.fail(1013, "已经有用户登录，请不要重复登录", "已经有用户登录，请退出登录后再次登录");
        }
        //使用BCrypt完成密码的匹配验证，如果匹配上了，需要完成原本由security自动完成的鉴权成功信息存储。
        if (new BCryptPasswordEncoder().matches(user.getPassword(), existUser.getPassword())) {
            //创建基于用户名密码完成鉴权的信息，一个token令牌存储用户名密码的信息
            UsernamePasswordAuthenticationToken securityToken = new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword());
            //基于token产生鉴权信息
            Authentication authentication = authenticationManager.authenticate(securityToken);
            //security完成鉴权之后的信息，存储再 SecurityContext中，将鉴权信息Authentication存进去
            SecurityContextHolder.getContext().setAuthentication(authentication);

            //生成token并且在返回头中返回
            String token = jwtUtils.generateToken(existUser);
            System.out.println("生成的token是" + token);

            //设置返回头也叫Authorization
            response.addHeader(JwtUtils.HEADER_STRING, token);
            //在跨域的情况下，依然能够正常返回Authorization
            response.addHeader("Access-Control-Expose-Headers", JwtUtils.HEADER_STRING);

            return Result.success("登录成功", "登录成功");
        }
        return Result.fail(1012, "用户名密码错误，登录失败", "用户名密码错误，登录失败");
    }

    @PostMapping("/register")
    public Result register(@RequestBody SunnyUser registerUser) {
        if (registerUser == null)
            return Result.fail(1021, "请输入用户信息注册！", "请输入正确用户信息注册！");
        if (registerUser.getUsername() == null || registerUser.getPassword() == null)
            return Result.fail(1022, "用户名和密码是必填选项", "用户名和密码是必填选项");
        if (registerUser.getUsername().length() < 3 || registerUser.getUsername().length() > 18)
            return Result.fail(1022, "用户名长度请控制在3到18位之间", "用户名长度请控制在3到18位之间");
        SunnyUser sunnyUser = sunnyUserService.loadUserByUsername(registerUser.getUsername());
        if (sunnyUser != null)
            return Result.fail(1023, "用户已经被注册", registerUser.getUsername());
        if (registerUser.getPassword().length() < 3 || registerUser.getPassword().length() > 18)
            return Result.fail(1024, "密码长度请控制在3到18位之间", "密码长度请控制在3到18位之间");
        //存储密码的时候，存储的是通过bcrypt加密后的信息。
        String encodedPassword = new BCryptPasswordEncoder().encode(registerUser.getPassword());
        registerUser.setPassword(encodedPassword);
        //将id置为0，让它自动递增。
        registerUser.setId(0L);
        //设置默认头像位置
        registerUser.setAvatar("avatar/向日葵卡通.jpg");
        boolean save = sunnyUserService.save(registerUser);
        if (save) {
            return Result.success("用户注册成功");
        } else {
            return Result.fail(1025, "用户注册失败，请检查服务器日志", null);
        }
    }

    @PutMapping
    public Result updateUser(@RequestHeader(JwtUtils.HEADER_STRING) String token, @RequestBody SunnyUser sunnyUser) {

        String usernameFromToken = jwtUtils.getUsernameFromToken(token.substring(JwtUtils.TOKEN_PREFIX.length()));
        SunnyUser existUser = sunnyUserService.loadUserByUsername(usernameFromToken);
        if (!existUser.getUsername().equals(sunnyUser.getUsername())) {
            System.out.println(existUser.getUsername());
            System.out.println(sunnyUser.getUsername());
            return Result.fail(1031, "不能修改其它用户的信息", "不能修改其它用户的信息");
        }
        //用户更新的时候，只更新其中的昵称部门和性别。
        boolean update = sunnyUserService.update(new LambdaUpdateWrapper<SunnyUser>()
                //更新的时候，根据用户名来进行修改
                .eq(SunnyUser::getUsername, sunnyUser.getUsername())
                .set(SunnyUser::getNickname, sunnyUser.getNickname())
                .set(SunnyUser::getDept, sunnyUser.getDept())
                .set(SunnyUser::getGender, sunnyUser.getGender())
        );
        if (update) {
            return Result.success("更新用户信息成功");
        } else {
            return Result.fail(1032, "更新用户信息失败", "更新用户信息失败");
        }


    }

    @PutMapping("/password")
    public Result updatePassword(@RequestHeader(JwtUtils.HEADER_STRING) String token, @RequestBody SunnyUser sunnyUser) {
        String usernameFromToken = jwtUtils.getUsernameFromToken(token.substring(JwtUtils.TOKEN_PREFIX.length()));
        SunnyUser existUser = sunnyUserService.loadUserByUsername(usernameFromToken);
        if (!existUser.getUsername().equals(sunnyUser.getUsername())) {
            return Result.fail(403, "用户信息不匹配！请检查", "用户信息不匹配！请检查");
        }
        //用户更新的时候，只更新密码，其中的密码记得bCrypt加密
        boolean update = sunnyUserService.update(new LambdaUpdateWrapper<SunnyUser>()
                //更新的时候，根据用户名来进行修改
                .eq(SunnyUser::getUsername, sunnyUser.getUsername())
                .set(SunnyUser::getPassword, new BCryptPasswordEncoder().encode(sunnyUser.getPassword()))
        );
        if (update) {
            return Result.success("修改密码成功");
        } else {
            return Result.fail(413, "修改密码失败", "更新用户信息失败");
        }
    }


    /**
     * 将登出接口中的token存到黑名单里，表示已经失效。
     *
     * @param token
     * @return
     */
    @GetMapping("/logout")
    public Result logout(@RequestHeader(JwtUtils.HEADER_STRING) String token) {
        //清理context中存储的鉴权信息
        SecurityContextHolder.clearContext();
        //将token置为失效
        jwtUtils.invalidateToken(token);
        return Result.success("注销成功");
    }

    @ApiOperation("获取头像")
    @GetMapping("/avatar/{userId}")
    public ResponseEntity getAvatar(@PathVariable Long userId) {
        //基于请求Id查询shotPath
        String avatarPath = sunnyUserService.getById(userId).getAvatar();
        System.out.println(avatarPath);
        if (avatarPath == null || avatarPath.trim().length() == 0) {
            return ResponseEntity.notFound().build();
        }
        File imgFile = new File(avatarPath);
        //如果文件不存在则返回notFound
        if (!imgFile.exists()) {
            return ResponseEntity.notFound().build();
        }
        try {
            byte[] imageBytes = Files.readAllBytes(imgFile.toPath());
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.IMAGE_PNG);
            return ResponseEntity.ok()
                    .headers(headers)
                    .body(imageBytes);
        } catch (IOException e) {
            log.error("获取文件图片失败", e.fillInStackTrace());
            return ResponseEntity.badRequest().body("获取文件图片失败");
        }

    }


    @PutMapping("/avatar")
    public Result uploadAvatar(@RequestHeader(JwtUtils.HEADER_STRING) String token, @RequestParam("avatar") MultipartFile avatarFile, HttpServletRequest request) {

        //判断文件是否是一个图片文件
        try {
            if (!sunnyUserService.isImage(avatarFile.getInputStream())) {
                return Result.fail(1052, "上传文件失败", "文件不是图片格式");
            }
        } catch (IOException e) {
            log.error("读取文件失败", e.fillInStackTrace());
            return Result.fail(1051, "上传文件失败", "读取文件出错");
        }
        //上传文件，放到服务器运行的临时目录下
        //获取项目的运行目录下的avatar目录
        String realPath = request.getSession().getServletContext().getRealPath("/avatar");
        //如果没有avatar目录，创建它
        File file = new File(realPath);
        if (!file.isDirectory()) {
            file.mkdir();
        }
        //生成文件名
        String originalFilename = avatarFile.getOriginalFilename();
        //拼接用户名作为文件名的唯一标识
        String usernameFromToken = jwtUtils.getUsernameFromToken(token.substring(JwtUtils.TOKEN_PREFIX.length()));
        String filename = usernameFromToken + originalFilename;
        //拼接文件的绝对路径
        String filePath = realPath + File.separator + filename;
        //文件保存到指定的路径下
        try {
            avatarFile.transferTo(new File(filePath));
            //将文件路径更新到数据库中存储
            SunnyUser sunnyUser = sunnyUserService.loadUserByUsername(usernameFromToken);
            sunnyUser.setAvatar(filePath);
            boolean update = sunnyUserService.updateById(sunnyUser);
            if (update) {
                return Result.success("上传头像成功");
            } else {
                return Result.fail(1052, "上传头像成功，但修改用户头像路径失败", "上传头像成功，但修改用户头像路径失败");
            }
        } catch (IOException e) {
            log.error("保存头像失败", e.fillInStackTrace());
            return Result.fail(1053, "上传头像失败", "文件无法拷贝到指定目录下");
        }
    }


}